package com.kexilo.system.auth.service;

import com.kexilo.core.common.core.web.domain.R;
import com.kexilo.core.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 验证码服务
 * 
 * @author Kexilo
 */
@Service
public class CaptchaService {

    private static final Logger log = LoggerFactory.getLogger(CaptchaService.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 验证码字符集
    private static final String CAPTCHA_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
    
    // 验证码长度
    private static final int CAPTCHA_LENGTH = 4;
    
    // 图片宽度
    private static final int IMG_WIDTH = 130;
    
    // 图片高度
    private static final int IMG_HEIGHT = 48;
    
    // 验证码有效期（分钟）
    private static final int CAPTCHA_EXPIRE_TIME = 2;
    
    // 干扰线数量
    private static final int INTERFERENCE_LINES = 40;
    
    // 干扰点数量
    private static final int INTERFERENCE_POINTS = 100;

    /**
     * 生成验证码
     * 
     * @return 验证码信息
     */
    public R<CaptchaInfo> generateCaptcha() {
        try {
            // 生成验证码文本
            String captchaText = generateCaptchaText();
            
            // 生成验证码图片
            BufferedImage image = generateCaptchaImage(captchaText);
            
            // 转换为Base64
            String base64Image = imageToBase64(image);
            
            // 生成验证码key
            String captchaKey = UUID.randomUUID().toString();
            
            // 存储到Redis
            String redisKey = "captcha:" + captchaKey;
            redisTemplate.opsForValue().set(redisKey, captchaText.toLowerCase(), 
                                          CAPTCHA_EXPIRE_TIME, TimeUnit.MINUTES);
            
            // 返回验证码信息
            CaptchaInfo captchaInfo = new CaptchaInfo();
            captchaInfo.setCaptchaKey(captchaKey);
            captchaInfo.setCaptchaImage("data:image/png;base64," + base64Image);
            
            log.debug("生成验证码: key={}, text={}", captchaKey, captchaText);
            
            return R.success(captchaInfo);
            
        } catch (Exception e) {
            log.error("生成验证码失败", e);
            return R.error("生成验证码失败");
        }
    }

    /**
     * 验证验证码
     * 
     * @param captchaKey 验证码key
     * @param captchaText 用户输入的验证码
     * @return 验证结果
     */
    public boolean validateCaptcha(String captchaKey, String captchaText) {
        if (StringUtils.isEmpty(captchaKey) || StringUtils.isEmpty(captchaText)) {
            return false;
        }
        
        try {
            String redisKey = "captcha:" + captchaKey;
            String storedCaptcha = (String) redisTemplate.opsForValue().get(redisKey);
            
            if (StringUtils.isEmpty(storedCaptcha)) {
                log.debug("验证码已过期或不存在: key={}", captchaKey);
                return false;
            }
            
            // 验证成功后删除验证码（一次性使用）
            redisTemplate.delete(redisKey);
            
            boolean isValid = storedCaptcha.equalsIgnoreCase(captchaText.trim());
            log.debug("验证码验证结果: key={}, input={}, stored={}, result={}", 
                     captchaKey, captchaText, storedCaptcha, isValid);
            
            return isValid;
            
        } catch (Exception e) {
            log.error("验证验证码失败", e);
            return false;
        }
    }

    /**
     * 生成验证码文本
     * 
     * @return 验证码文本
     */
    private String generateCaptchaText() {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        
        for (int i = 0; i < CAPTCHA_LENGTH; i++) {
            int index = random.nextInt(CAPTCHA_CHARS.length());
            sb.append(CAPTCHA_CHARS.charAt(index));
        }
        
        return sb.toString();
    }

    /**
     * 生成验证码图片
     * 
     * @param text 验证码文本
     * @return 验证码图片
     */
    private BufferedImage generateCaptchaImage(String text) {
        BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = image.createGraphics();
        
        // 设置抗锯齿
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        // 填充背景色
        g2d.setColor(getRandomColor(220, 250));
        g2d.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);
        
        // 绘制干扰线
        drawInterferenceLines(g2d);
        
        // 绘制验证码文本
        drawCaptchaText(g2d, text);
        
        // 绘制干扰点
        drawInterferencePoints(g2d);
        
        g2d.dispose();
        
        return image;
    }

    /**
     * 绘制干扰线
     * 
     * @param g2d 图形对象
     */
    private void drawInterferenceLines(Graphics2D g2d) {
        Random random = new Random();
        
        for (int i = 0; i < INTERFERENCE_LINES; i++) {
            int x1 = random.nextInt(IMG_WIDTH);
            int y1 = random.nextInt(IMG_HEIGHT);
            int x2 = random.nextInt(IMG_WIDTH);
            int y2 = random.nextInt(IMG_HEIGHT);
            
            g2d.setColor(getRandomColor(160, 200));
            g2d.drawLine(x1, y1, x2, y2);
        }
    }

    /**
     * 绘制验证码文本
     * 
     * @param g2d 图形对象
     * @param text 验证码文本
     */
    private void drawCaptchaText(Graphics2D g2d, String text) {
        Random random = new Random();
        
        // 字体名称数组
        String[] fontNames = {"Arial", "Times New Roman", "Courier New", "Verdana"};
        
        for (int i = 0; i < text.length(); i++) {
            // 随机选择字体
            String fontName = fontNames[random.nextInt(fontNames.length)];
            int fontStyle = random.nextBoolean() ? Font.BOLD : Font.PLAIN;
            int fontSize = 20 + random.nextInt(8);
            
            g2d.setFont(new Font(fontName, fontStyle, fontSize));
            g2d.setColor(getRandomColor(20, 130));
            
            // 计算字符位置
            int x = 25 + i * 20 + random.nextInt(8);
            int y = 25 + random.nextInt(8);
            
            // 旋转角度
            double angle = (random.nextDouble() - 0.5) * 0.4;
            g2d.rotate(angle, x, y);
            
            // 绘制字符
            g2d.drawString(String.valueOf(text.charAt(i)), x, y);
            
            // 恢复旋转
            g2d.rotate(-angle, x, y);
        }
    }

    /**
     * 绘制干扰点
     * 
     * @param g2d 图形对象
     */
    private void drawInterferencePoints(Graphics2D g2d) {
        Random random = new Random();
        
        for (int i = 0; i < INTERFERENCE_POINTS; i++) {
            int x = random.nextInt(IMG_WIDTH);
            int y = random.nextInt(IMG_HEIGHT);
            
            g2d.setColor(getRandomColor(150, 200));
            g2d.fillOval(x, y, 2, 2);
        }
    }

    /**
     * 获取随机颜色
     * 
     * @param fc 颜色下限
     * @param bc 颜色上限
     * @return 随机颜色
     */
    private Color getRandomColor(int fc, int bc) {
        Random random = new Random();
        
        if (fc > 255) fc = 255;
        if (bc > 255) bc = 255;
        
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        
        return new Color(r, g, b);
    }

    /**
     * 图片转Base64
     * 
     * @param image 图片
     * @return Base64字符串
     * @throws IOException
     */
    private String imageToBase64(BufferedImage image) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "png", baos);
        byte[] bytes = baos.toByteArray();
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 验证码信息类
     */
    public static class CaptchaInfo {
        private String captchaKey;
        private String captchaImage;

        public String getCaptchaKey() {
            return captchaKey;
        }

        public void setCaptchaKey(String captchaKey) {
            this.captchaKey = captchaKey;
        }

        public String getCaptchaImage() {
            return captchaImage;
        }

        public void setCaptchaImage(String captchaImage) {
            this.captchaImage = captchaImage;
        }
    }
}
